---
title: 编写Spring应用
---
为 Spring Boot 应用添加一个主页。

* 一个控制器类，用来处理主页相关的请求；
* 一个视图模板，用来定义主页看起来是什么样子。
* 编写一个简单的测试类来测试主页

## 处理Web请求

Spring MVC 的核心是控制器（controller）的理念。控制器是处理请求并以某种方式进行信息响应的类。
```java  title='主页控制器'
@Controller //控制器
public class HomeController {
    //处理对根路径  / 的请求
    @GetMapping("/")
    public String home() {
        //返回视图名
        return "home";
    }
}
```
可以看到，这个类带有 `@Controller` 注解，它的主要目的是让组件扫描将这个类识别为一个组件。

Spring 的组件扫描功能会自动发现它，并创建一个 HomeController 实例作为 Spring 应用上下文中的 bean。

实际上，`@Component`、`@Service`、`@Repository` 注解有着与 `@Controller` 类似的作用，这里使用 `@Controller` 更能描述这个这个组件在应用中的角色。

home() 是一个简单的控制器方法。它带有 `@GetMapping` 注解，表明如果针对 "/" 发送 HTTP GET 请求，那么将会由这个方法来处理请求。该方法所做的只是返回 String类型的 home 值。

这个值会解析为视图的逻辑名。因为 Thymeleaf 位于类路径中，所以可以使用 Thymeleaf 来定义模板。

模板名称是由逻辑视图名派生而来的，再加上 "/templates/" 前缀和 ".html" 后缀。最终形成的模板路径是 "/templates/home.html"。所以，我们需要将模板放到项目的 "/src/main/resources/templates/home.html" 中。



## 定义视图

```html title = '/src/main/resources/templates/home.html'
<!doctype html>
<html lang="zh-cn"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
>
<head>
    <title>Cloud</title>
</head>
<body>
<h1>Welcome to ...</h1>
<img th:src="@{/images/logo.png}">
</body>
</html>
```
`<img>`标签使用了 Thymeleaf 的 th:src 属性和 @{...}表达式，以便于引用相对于上下文路径的图片。图片是相对于上下文 `/images/cloud.png` 路径来引用的。
像图片这样的资源位于 ”/src/main/resources/static“ 文件夹。

## 测试控制器
```java
package pub.wkq.springboot.controller;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import static org.hamcrest.Matchers.containsString;

@WebMvcTest(HomeController.class) //针对 HomeController 的 web 测试
public class HomeControllerTest {
    @Autowired
    private MockMvc mockMvc;


    @Test
    public void testHomePage() throws Exception {
        mockMvc.perform(get("/"))//发起对 /的 get 请求
                .andExpect(status().isOk()) //期望得到 http 200
                .andExpect(view().name("home"))//期望得到 hone 视图
                .andExpect(content().string(containsString("Welcome to ...")))
        ;
    }
}
```
HomeControllerTests 没有使用 `@SpringBootTest` 注解，而是添加了 `@WebMvcTest` 注解。这是 Spring Boot 提供的一个特殊的测试注解，让这个测试在 Spring MVC 上下文中执行。具体来讲，它会将 HomeController 注册到 Spring
MVC 中，这样一来，我们就可以向它发送请求了。

`@WebMvcTest`同样会为 Spring MVC 应用提供了 Spring 环境的支持。

通过 testHomePage() 方法，定义了针对主页想要执行的测试。它首先使用 MockMvc 对象对  "/" 发起 HTTP  GET 请求。对于这个请求，设置了如下预期：
1. 响应状态码为 200
2. 视图的逻辑名应该是 home
3. 渲染后的视图应该包含文本 “Welcome to ...”


可以在 IDEA 中运行测试，也可以使用如下的 Maven 命令。
```sh
mvnw test
```
如果在 MockMvc 对象发送请求之后，上述预期没有全部满足，那么这个测试就会失败。

## Spring Boot DevTools
DevTools 为 Spring 开发人员提供了一些便利的开发工具和特性，其中包括：
* 代码变更后应用自动重启；
* 当面向浏览器的资源（如模板、JavaScript、样式表）等发生变化时，会自动刷新浏览器；
* 自动禁用模板缓存；
* 如果使用 H2 数据库，则内置了 H2 控制台；
因为它的用途仅仅是开发，所以它能很智能地在生产环境把自己禁用掉。
### 应用重启
当 DevTools 启用的时候，应用程序会加载到 Java 虚拟机中两个独立的类加载器中。其中一个类加载器会加载Java代码、属性文件，以及项目的“src/main/”路径下几乎所有的内容。这些可能会经常发生变化。

另一个类加载器会加载依赖的库，这些库不太可能经常发生变化。

当探测到变更的时候，DevTools 只会重新加载包含项目代码的类加载器，并重启 Spring 的应用上下文，在这个过程中，另外一个类加载器和 JVM 会原封不动。
这个策略非常精细，但会减少应用启动的时间。

这个策略的一个不足之处就是自动重启无法反映依赖项的变化。这时因为包含依赖库的类加载器不回自动加载。所以变更依赖时，为了让其生效，都要重启应用。

### 内置的 H2 控制台
如果使用H2数据库进行开发，DevTools 将会自动启用 H2 控制台，访问地址为： http://localhost:8080/h2-console

## 总结
在 pom.xml 文件中，我们声明了对 Web 和 Thymeleaf starter 的依赖。这两项依赖会传递引入大量其它的依赖，包括：
* Spring 的 MVC 框架；
* 嵌入式的 Tomcat
* Thymeleaf 和 Thymeleaf 布局方言

它还引入了 Spring Boot 自动配置库。当应用启动的时候，Spring Boot 的自动配置将会探测到这些库，并自动完成如下功能：

* 在 Spring 应用上下文中配置 bean 以启用 Spring MVC；
* 在 Spring 应用上下文中配置嵌入式的 Tomcat 服务器；
* 配置 Thymeleaf 视图解析器以便使用 Thymeleaf 模板渲染 Spring MVC 视图；
